package com.rfid.manager;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;

import com.hoho.android.usbserial.driver.UsbId;
import com.hoho.android.usbserial.driver.UsbSerialDriver;
import com.hoho.android.usbserial.driver.UsbSerialProber;
import com.hoho.android.usbserial.util.SerialInputOutputManager;
import com.rfid.a252robot.config.GlobalConfig;
import com.rfid.common.log.LogUtils;
import com.rfid.common.util.ByteUtil;
import com.rfid.util.Util;
import com.shrfid.module_base.entity.TagEntity;
import com.shrfid.rfidsdk.tag.base.BaseDecoder;
import com.spectre.generate.ConfigManager;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public enum  DoorBoxManager implements SerialInputOutputManager.Listener {

    INSTANCE;
    private static final String TAG = "DoorBoxManager";
    private UsbManager mUsbManager;
    private UsbDevice mUsbDevice;
    private UsbSerialDriver mUsbSerialDriver;
    private SerialInputOutputManager mSerialIoManager;
    private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
    byte buffer[] = new byte[5];
    Handler mHandler = new Handler();
    Context mContext;
    List<Listener> mListeners = new ArrayList<>();
    int pos = 0;
    List<CountDownLatchProx> mCountDownLatchProxes = new ArrayList<>();

    public void init(Context context) {
        System.out.println("init DoorBoxManager context");
        mContext = context;
        init();
    }

    private void init() {
        if (mContext == null) {
            return;
        }
        System.out.println("init DoorBoxManager");
        mUsbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
        for (final UsbDevice device : mUsbManager.getDeviceList().values()) {
            int vendorId = device.getVendorId();
            int productId = device.getProductId();
            String s = device.getSerialNumber();
            System.out.println(device.getProductName() + ":" + productId + ":" + vendorId);
            GlobalConfig config = ConfigManager.INSTANCE.getGlobalConfig();
            String ds = config.getDoorSerialNumber();
            if (productId == Integer.parseInt(config.getDoorPid(),16)&&vendorId == Integer.parseInt(config.getDoorVid(),16)){
                if (!TextUtils.isEmpty(ds)) {
                    if (!ds.equals(s)) {
                        continue;
                    }
                }
                if (mUsbManager.hasPermission(device)) {
                    LogUtils.v("DoorBoxManager init find device");
                    mUsbDevice = device;
                    break;
                } else {
                    requestPermission(device);
                }
            }
        }
    }

    public void requestPermission(UsbDevice device) {
        LogUtils.v("device request permission " + device.getDeviceName());
        UsbPermissionReceiver usbPermissionReceiver = new UsbPermissionReceiver();
        Intent intent = new Intent(UsbPermissionReceiver.ACTION_DEVICE_PERMISSION);
        PendingIntent mPermissionIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
        IntentFilter permissionFilter = new IntentFilter(UsbPermissionReceiver.ACTION_DEVICE_PERMISSION);
        mContext.registerReceiver(usbPermissionReceiver, permissionFilter);
        mUsbManager.requestPermission(device, mPermissionIntent);
    }

    public boolean checkDevice(int vendorId1,int productId1) {
        UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
        for (final UsbDevice device : usbManager.getDeviceList().values()) {
            int vendorId = device.getVendorId();
            int productId = device.getProductId();
            System.out.println(device.getProductName() + ":" + productId + ":" + vendorId);
            if (productId == productId1 && vendorId == vendorId1){
                if (usbManager.hasPermission(device)) {
                    LogUtils.v("DoorBoxManager init find device");
                    final List<UsbSerialDriver> drivers = UsbSerialProber.probeSingleDevice(mUsbManager, device);
                    if (!drivers.isEmpty()) {
                        UsbSerialDriver usbSerialDriver = drivers.get(0);
                        try {
                            usbSerialDriver.open();
                            usbSerialDriver.setParameters(9600, 8, UsbSerialDriver.STOPBITS_1, UsbSerialDriver.PARITY_NONE);
                            return true;
                        } catch (Exception e) {
                            return false;
                        }
                    } else {
                        System.out.println("drivers is empty");
                        return false;
                    }
                } else {
                    return false;
                }
            }
        }
        return false;
    }

    public boolean isConnect() {
        return mUsbDevice != null;
    }

    public void prepareReadWrite() {
        LogUtils.v("prepareReadWrite");
        if (mUsbDevice == null) {
            init();
        }
        if (mUsbDevice != null) {
            try {
                System.out.println("begain probeSingleDevice");
                final List<UsbSerialDriver> drivers = UsbSerialProber.probeSingleDevice(mUsbManager, mUsbDevice);
                System.out.println("end probeSingleDevice");
                if (!drivers.isEmpty()) {
                    mUsbSerialDriver = drivers.get(0);
                    mUsbSerialDriver.open();
                    mUsbSerialDriver.setParameters(9600, 8, UsbSerialDriver.STOPBITS_1, UsbSerialDriver.PARITY_NONE);
                    startRead();
                } else {
                    System.out.println("drivers is empty");
                    mUsbDevice = null;
                }
            } catch (Exception e) {
                System.out.println("prepareReadWrite error!");
                e.printStackTrace();
            }
        }
    }

    public void openAllLock() {
        synchronized (this) {
            if (mSerialIoManager == null || mSerialIoManager.getState() == SerialInputOutputManager.State.STOPPED) {
                prepareReadWrite();
            }
            LogUtils.v("openLock openAllLock");
            int par[] = Util.getAllLockNums();
            for (int pos : par) {
                System.out.println("openAll pos = " + pos);
                byte cmd[] = new byte[]{(byte) 0xF7, (byte) pos, 0x01, 0x07, 0x7F};
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (mSerialIoManager != null) {
                    mSerialIoManager.writeAsync(cmd);
                }
            }
        }
    }

    public void openLock(byte pos) {
        synchronized (this) {
            if (mSerialIoManager == null || mSerialIoManager.getState() == SerialInputOutputManager.State.STOPPED) {
                prepareReadWrite();
            }
            LogUtils.v("openLock pos = " + pos);
            byte cmd[] = new byte[]{(byte) 0xF7, pos, 0x01, 0x01, 0x7F};
            if (mSerialIoManager != null) {
                mSerialIoManager.writeAsync(cmd);
            }
        }
    }

    public void queryOpenStatus(byte pos) {
        synchronized (this) {
            if (mSerialIoManager == null || mSerialIoManager.getState() == SerialInputOutputManager.State.STOPPED) {
                prepareReadWrite();
            }
            byte cmd[] = new byte[]{(byte) 0xF7, pos, 0x02, 0x01, 0x7F};
            if (mSerialIoManager != null) {
                System.out.println(mSerialIoManager.getState());
                mSerialIoManager.writeAsync(cmd);
            }
        }
    }

    public int getLockOpenStatus(int pos) {
        CountDownLatchProx prox = new CountDownLatchProx(pos);
        mCountDownLatchProxes.add(prox);
        queryOpenStatus((byte) pos);
        try {
            prox.await(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
            return -1;
        }finally {
            mCountDownLatchProxes.remove(prox);
        }
        if (prox.latch.getCount() > 0) {
            return -1;
        }
        return prox.open?1:0;
    }

    private void startRead() {
        System.out.println("startRead");
        stopIoManager();
        startIoManager();
    }

    private void stopIoManager() {
        synchronized (this) {
            if (mSerialIoManager != null) {
                LogUtils.i(TAG, "Stopping io manager ..");
                mSerialIoManager.stop();
                mSerialIoManager = null;
            }
        }
    }

    private void startIoManager() {
        synchronized (this) {
            if (mUsbSerialDriver != null) {
                if (mSerialIoManager != null) {
                    mSerialIoManager.setListener(null);
                }
                mSerialIoManager = new SerialInputOutputManager(mUsbSerialDriver, this);
                LogUtils.v( "Starting io manager .." + mSerialIoManager);
                mExecutor.submit(mSerialIoManager);
            }
        }
    }

    @Override
    public void onNewData(byte[] data) {
        System.out.println("onNewData size = " + data.length + "------ " + ByteUtil.bytesToHexString(data));
        dispatch(data);
    }

    @Override
    public void onRunError(Exception e) {
        e.printStackTrace();
        //加了USB監聽后，此处可有可无了，此处容易导致错误死循环
//        mHandler.postDelayed(new Runnable() {
//            @Override
//            public void run() {
//                init();
//                prepareReadWrite();
//            }
//        },5000);
    }

    private synchronized void dispatch(byte[] bytes) {
        for (byte b : bytes) {
            if ((b&0xFF) == 0xF7) {
                System.out.println("new buffer");
                buffer = new byte[5];
                buffer[0] = b;
                pos = 1;
            } else {
                buffer[pos] = b;
                pos ++;
            }
            if (pos == 5){
                break;
            }
        }
        if (pos != 5) {
            return;
        }
        byte b = buffer[0];
        if ((b&0XFF) == 0xF7 && (buffer[4]&0xFF) == 0x7F) {
            int num = buffer[1]&0XFF;
            int cmd = buffer[2]&0XFF;
            int r = buffer[3]&0XFF;
            System.out.println(cmd + ":" + r);
            if (cmd == 0x01) {//开门的反馈，板子与文档结果反了
                LogUtils.v(Util.getBoxShow(num) + (r == 0xFF?"开成功":"门失败"));
                onOpenResult(num,r == 0xFF);
            } else if (cmd == 0x02) {//查询单个锁的结果反馈，板子与文档结果反了
                LogUtils.v(Util.getBoxShow(num) + (r == 0x00?"门开":"门关"));
                onLockOpen(num,r == 0x00);
            }

        }
    }

    private void onLockOpen(int num, boolean open) {
        synchronized (mListeners) {
            for (Listener listener : mListeners) {
                listener.onLockOpenStatus(num, open);
            }
        }
        for (CountDownLatchProx prox:mCountDownLatchProxes) {
            if (num == prox.boxNum) {
                prox.countDown(open);
            }
        }
    }

    private void onOpenResult(int num, boolean success) {
        synchronized (mListeners) {
            for (Listener listener : mListeners) {
                listener.onLockOpen(num, success);
            }
        }
    }

    public void addListener(Listener listener) {
        synchronized (mListeners) {
            if (!mListeners.contains(listener)) {
                mListeners.add(listener);
            }
        }
    }

    public void removeListener(Listener listener) {
        synchronized (mListeners) {
            mListeners.remove(listener);
        }
    }

    class CountDownLatchProx {
        CountDownLatch latch;
        int boxNum;
        boolean open;
        public CountDownLatchProx(int box) {
            boxNum = box;
            latch = new CountDownLatch(1);
        }

        public void countDown(boolean open) {
            this.open = open;
            latch.countDown();
        }

        public void await(int sec) throws InterruptedException {
            latch.await(sec, TimeUnit.SECONDS);
        }

    }

    public interface Listener{
        /*执行开门的状态回调*/
        public void onLockOpen(int num, boolean success);
        /*查询门锁状态的回馈*/
        public void onLockOpenStatus(int num, boolean open);
    }
}
